<?php
/**
 * 观察者模式
 *
 * 定义了一种一对多的依赖关系，让多个观察者对象同时监听某一个主体对象。
 * 这个对象在状态上发生变化时，会通知所有观察者对象，使他们能自动更新自己。
 */
//抽象主题角色
abstract class Subject{
	//所有观察者
	protected $observers = array();
	//添加观察者
	public function attach(Observer $observer)
	{
		//构建一个只有当前节点的数组。
		$other_list = array($observer);
		//获得原节点结构与当前节点结构的差集
		$result_list = array_uintersect($this->observers,$other_list,array(__CLASS__,"key_compare_func"));
		//如果不存在相同的，则添加。
		if(empty($result_list)){
			array_push($this->observers,$observer);
		}
	}
	//删除观察者
	public function detach(Observer $observer){
		//构建一个只有当前节点的数组。
		$other_list = array($observer);
		//获得原节点结构与当前节点结构的差集
		$result_list = array_udiff($this->observers,$other_list,array(__CLASS__,"key_compare_func"));
		//将原先节点置为差集
		$this->observers = $result_list;
	} 
	//通知观察者变化
	protected function Notify()
	{
		foreach($this->observers as $observer)
		{
			$observer->Update();
		}
	}
	//比较的方法
	public function key_compare_func($a,$b)
	{
		if(is_object($a)&&is_object($b)&&$a->name === $b->name){
			return 0;
		}
		return ($a > $b)? 1 :-1;
	}
	public function getObsevers()
    {
        return $this->observers;
    }
}
//具体主题角色
class ConcreteSubject extends Subject{
	//状态值
	private $subjectState;
	//设置方法
	public function __set($name,$value)
	{
		$this->$name = $value;
		//通知观察者
		$this->Notify();
	}
	//获取状态
	public function __get($name)
	{
		return $this->$name;
	}
}
//抽象观察者
abstract class Observer{
	public $name;
	protected $subject;
	protected $observerState;
	public function __construct(Subject $subject,$name)
	{
		$this->subject = $subject;
		$this->name = $name;
	}
	public function getObserverState()
	{
		return $this->observerState;
	}
	abstract public function Update();
}
//杀手观察者
class Killer extends Observer{
	//更新状态
	public function Update(){
		$subjectState = $this->subject->subjectState;
		switch($subjectState){
			case "alive": $this->observerState = "kill you";break;
			case "dead" : $this->observerState = "very good";break;
			default: $this->observerState = "nothing";
		}
	}
}
//路人观察者
class Man extends Observer{
	//更新状态
	public function Update(){
		$subjectState = $this->subject->subjectState;
		switch($subjectState){
			case "alive": $this->observerState = "nothing";break;
			case "dead" : $this->observerState = "some one dead";break;
			default: $this->observerState = "nothing";
		}
	}
}
//亲戚
class Relation extends Observer{
	//更新状态
	public function Update()
	{
		$subjectState = $this->subject->subjectState;
		switch($subjectState){
			case "alive": $this->observerState = "I can get many money";break;
			case "dead" : $this->observerState = "some money gone";break;
			default: $this->observerState = "nothing";
		}
	}
}

$subject = new ConcreteSubject();
$killerA = new Killer($subject,"killerA");
$subject->attach($killerA);
$subject->attach(new Killer($subject,"killerB"));
$subject->attach(new Man($subject,"ManA"));
$subject->attach(new Man($subject,"ManB"));
$subject->attach(new Relation($subject,"RelationA"));
$subject->attach(new Relation($subject,"RelationB"));
echo "-----------when he is alive------------".PHP_EOL;
$subject->subjectState = "alive";

foreach($subject->getObsevers() as $observer)
{
	$observerState = $observer->getObserverState();
	echo "$observer->name said:$observerState".PHP_EOL;
}
echo "-----------when he is dead------------".PHP_EOL;
$subject->detach($killerA);
$subject->subjectState = "dead";

foreach($subject->getObsevers() as $observer)
{
	$observerState = $observer->getObserverState();
	echo "$observer->name said:$observerState".PHP_EOL;
}
